//
//  Peers.m
//  SolarSeek
//
//  Created by Erick Fejta on 05/01/17.
//  Copyright 2005 Erick Fejta. All rights reserved.
//

#import "Peers.h"
#import "Shares.h"
#import "SolarSeekEngine.h"

const unsigned PeerPrivilegesDonated	= 0x40000000; //people donating to Soulseek
const unsigned PeerPrivilegesSSDonated	= 0x10000000; //people donating to SolarSeek
const unsigned PeerPrivilegesSS			= 0x00030000; //people using SolarSeek
const unsigned PeerPrivilegesFriend		= 0x00040000; //your friends
const unsigned PeerPrivilegesBestFriend	= 0x00100000; //your closest friends	
const unsigned PeerPrivilegesAssociate	= 0x00000100; //someone you know a bit
const unsigned PeerPrivilegesUploaded	= 0x00000040; //someone who has sent info
const unsigned PeerPrivilegesNotAnnoying= 0x00010000; //anyone who isn't annoying.
const unsigned PeerPrivilegesTalked		= 0x00000010; //someone who said something.

unsigned PeerCount=0;

@implementation Peer
- (id)init { return [self initWithName:nil]; }
- (id)initWithName:(NSString *)n {
	if(n==nil)
		[NSException raise:NSInvalidArgumentException format:@"name cannot be nil."];
	if(self = [super init]) {
		name = [n copy];
		sharesTree = [[ShareTree alloc] initWithPathData:[NSData data]];
		privileges = PeerPrivilegesNotAnnoying;
		availability = AvailabilityUnknown;
		exists = YES;
		picture = nil;
		comment = nil;
		iconBuffer = nil;
		note = nil;
		address = nil;
	}
//	NSLog(@"ALLOC: %@(%d) allocated. %d peers. ",n,self,PeerCount++);
	return self;
}

- (id)initWithCoder:(NSCoder *)coder {
	if([coder allowsKeyedCoding]) {
		NSString *aname = [[coder decodeObjectForKey:@"name"] retain];
		Peer *p = [[NSApp engine] peerNamed:aname];
		[self release];
		self = [p retain];
		name = aname;
		exists = [coder decodeBoolForKey:@"exists"];
		token = [coder decodeInt32ForKey:@"token"];
		sharesTree = [[ShareTree alloc] initWithPathData:[NSData data]];
		if([coder containsValueForKey:@"shares"]) {
			id shares = [coder decodeObjectForKey:@"shares"]; //automatically added to peer.
		}
		privileges = [coder decodeInt32ForKey:@"privileges"];
		availability = AvailabilityUnknown;
		speedAverage = [coder decodeInt32ForKey:@"average speed"];
		countFiles = [coder decodeInt32ForKey:@"number of files"];
		countFolders = [coder decodeInt32ForKey:@"number of folders"];
		countSlotsAvailable = [coder decodeInt32ForKey:@"number of available slots"];
		countSlotsTotal = [coder decodeInt32ForKey:@"number of total slots"];
		countDownloadsQueued = [coder decodeInt32ForKey:@"number of queued downloads"];
		comment = [[coder decodeObjectForKey:@"comment"] retain];
		picture = [[coder decodeObjectForKey:@"picture"] retain];
		iconBuffer = nil;
		address = nil;
		note = [[coder decodeObjectForKey:@"note"] retain];
		return self;
	}
	else {
		[NSException raise:NSInvalidArchiveOperationException format:@"Only supports NSKeyedArchiver coders"];
	}
}


- (void)encodeWithCoder:(NSCoder *)coder {
	if([coder allowsKeyedCoding]) {
		[coder encodeBool:exists forKey:@"exists"];
		[coder encodeInt32:token forKey:@"token"];
		NSArray *a = [[self sharesTreeRoot] sharesThisAndSubLevels];
		[coder encodeObject:a forKey:@"shares"];
		[coder encodeObject:name forKey:@"name"];
		[coder encodeInt32:privileges forKey:@"privileges"];
		[coder encodeInt32:speedAverage forKey:@"average speed"];
		[coder encodeInt32:countFiles forKey:@"number of files"];
		[coder encodeInt32:countFolders forKey:@"number of folders"];
		[coder encodeInt32:countSlotsAvailable forKey:@"number of available slots"];
		[coder encodeInt32:countSlotsTotal forKey:@"number of total slots"];
		[coder encodeInt32:countDownloadsQueued forKey:@"number of queued downloads"];
		[coder encodeObject:comment forKey:@"comment"];
		[coder encodeObject:picture forKey:@"picture"];
		[coder encodeObject:note forKey:@"note"];
	}
	else {
		[NSException raise:NSInvalidArchiveOperationException format:@"Only supports NSKeyedArchiver coders"];
	}
}

- (void)dealloc { 
//	NSLog(@"DEALLOC: Deallocating %@(%d) %d peers.",name,self,--PeerCount);
	[sharesTree release];
	sharesTree = nil;
	[name release];
	name = nil;
	[picture release];
	picture = nil;
	[comment release];
	comment = nil;
	[iconBuffer release];
	iconBuffer = nil;
	[super dealloc];
}

- (id)copyWithZone:(NSZone *)zone { 
	return [self retain];
}

- (NSString *)description { return name; }

- (BOOL)isEqual:(id)obj {
	return ([obj isKindOfClass:[Peer class]] && [name isEqual:[obj name]]);
}

- (unsigned)hash { return [name hash]^NSNotFound; }

- (NSComparisonResult)compare:(Peer *)other {
	return [name caseInsensitiveCompare:[other name]];
}

- (id)valueForUndefinedKey:(NSString *)key {
	return self;
}

- (void)setAddress:(SolarSeekAddress *)a {
	[a retain];
	[address release];
	address = a;
}

- (void)setAvailability:(Availability)a {
	availability = a;
}
- (void)setComment:(NSString *)c { [c retain]; [comment release]; comment = c; }
- (void)setExists:(BOOL)e { exists = e; if(exists==NO) [self setAvailability:AvailabilityUnknown];}


- (void)setNote:(NSString *)n {
	[n retain];
	[note release];
	note = n;
}

- (void)setPicture:(NSData *)data {
	[self willChangeValueForKey:@"icon"];
	[data retain];
	[picture release];
	picture = data;
	[iconBuffer release];
	iconBuffer = nil;
	[self didChangeValueForKey:@"icon"];
}




- (SolarSeekAddress *)address { return address; }
- (Availability)availability { return availability; }
- (NSString *)comment { return comment; }
- (BOOL)exists { return exists; }
- (NSImage *)icon { 
	if(iconBuffer) return iconBuffer;
	else {
		if(picture) {
			iconBuffer = [[NSImage alloc] initWithData:picture];
		}
		if(iconBuffer==nil) {
			[picture release];
			picture = nil;
			iconBuffer = [[NSImage imageNamed:@"toolbarBrowse"] retain];
		}
		return iconBuffer;
	}
}
- (NSString *)name { return name; }
- (NSString *)note {  return note; }
- (NSData *)picture { return picture; }
- (unsigned)privileges { return privileges; }

- (unsigned)token { return token; }	


- (void)addPrivileges:(unsigned)privs { 
	//add priv bits.
	[self willChangeValueForKey:@"privileges"];
	privileges |= privs; 
	[self didChangeValueForKey:@"privileges"];
} 

- (void)removePrivileges:(unsigned)privs { 
	//remove priv bits
	[self willChangeValueForKey:@"privileges"];
	privileges = privileges & (privileges ^ privs); 
	[self didChangeValueForKey:@"privileges"];
} 


- (unsigned)countFiles {
	unsigned actual = [[self sharesTreeRoot] countSharesThisAndSubLevels];
	unsigned claim = countFiles;
	return actual*3 > claim?actual:claim;
}

- (unsigned)countFolders {
	unsigned actual = [[self sharesTreeRoot] countTreesFilledThisAndSubLevels];
	unsigned claim = countFolders;
	return actual*3 > claim?actual:claim;
}

- (void)removeAllShares {
	[self willChangeValueForKey:@"countFiles"];
	[sharesTree release];
	sharesTree = [[ShareTree alloc] initWithPathData:[NSData data]];
	[self didChangeValueForKey:@"countFiles"];
}

- (ShareTree *)sharesTreeRoot { return sharesTree; }

- (float)rating {
	float speed = speedAverage,
		  queuesize = countDownloadsQueued,
		  slotstot = countSlotsTotal,
		  slotsnow = countSlotsAvailable;
	if(slotsnow==0 && queuesize==0)
		return 0;
	if(slotstot == 0)
		slotstot = slotsnow==0?1:slotsnow;
	if(speed == 0)
		speed = 2048;
	float myspeed = speed * (slotsnow/slotstot) + 1;
	float mywait = queuesize / speed;
	
	return (myspeed - mywait);
}

+ (NSMutableSet *)peersInCollection:(id)collection {
	NSEnumerator *e = [collection objectEnumerator];
	id o;
	NSMutableSet *s = [NSMutableSet set];
	while(o = [e nextObject]) {
		[s addObject:[o peer]];
	}
	return s;
}

@end
